// --------------------------------------------------------------------------------------------------------------------
// <copyright file="CollectionAssert.cs" company="CSLib.Net Team">
//   Copyright (c) Roman "4ux-nbIx" Novitsky 2010
// </copyright>
// <summary>
//   Defines collections assertion methods.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

#define DEBUG

namespace CSLib.Net.Diagnostics
{
  #region Namespace Imports

  using System;
  using System.Collections.Generic;
  using System.Diagnostics;
  using System.Linq;

  using Annotations;

  #endregion

  // ReSharper disable ValueAnalysis

  /// <summary>
  /// Defines collections assertion methods.
  /// </summary>
  [DebuggerStepThrough]
  [DebuggerNonUserCode]
  [UsedImplicitly(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.WithMembers)]
  public static class CollectionAssert
  {
    #region Public Methods


    /// <summary>
    /// Asserts that a specified <see cref="IEnumerable{T}">collection</see> contains only unique items.
    /// </summary>
    /// <typeparam name="T">
    /// The type of collection items.
    /// </typeparam>
    /// <param name="collection">
    /// A <see cref="IEnumerable{T}">collection</see> to check.
    /// </param>
    /// <param name="messageFormat">
    /// A composite format string.
    /// </param>
    /// <param name="messageArguments">
    /// An array of message arguments to format.
    /// </param>
    [Conditional("DEBUG")]
    [StringFormatMethod("messageFormat")]
    public static void AllElementsAreUnique<T>(
      [NotNull] IEnumerable<T> collection,
      [NotNull] string messageFormat,
      [CanBeNull] params object[] messageArguments)
    {
      IEnumerable<T> distinct = collection.Distinct();

      if (distinct.Count() == collection.Count())
      {
        return;
      }

      string detailMessage = AssertionHelper.GetDetailMessage(messageFormat, messageArguments);

      Assert.Fail(detailMessage);
    }


    /// <summary>
    /// Asserts that a specified <see cref="IEnumerable{T}">collection</see> contains a specified item.
    /// </summary>
    /// <typeparam name="T">
    /// The type of collection items.
    /// </typeparam>
    /// <param name="collection">
    /// A <see cref="IEnumerable{T}">collection</see> to check.
    /// </param>
    /// <param name="item">
    /// An item to check.
    /// </param>
    /// <param name="messageFormat">
    /// A composite format string.
    /// </param>
    /// <param name="messageArguments">
    /// An array of message arguments to format.
    /// </param>
    [Conditional("DEBUG")]
    [StringFormatMethod("messageFormat")]
    public static void Contains<T>(
      [NotNull] IEnumerable<T> collection, 
      [NotNull] T item, 
      [NotNull] string messageFormat, 
      [CanBeNull] params object[] messageArguments)
    {
      string detailMessage = AssertionHelper.GetDetailMessage(messageFormat, messageArguments);

      Assert.IsTrue(collection.Contains(item), detailMessage);
    }


    /// <summary>
    /// Asserts that a specified <see cref="IEnumerable{T}">collection</see> contains an item defined by the predicate.
    /// </summary>
    /// <typeparam name="T">The type of collection items.</typeparam>
    /// <param name="collection">A <see cref="IEnumerable{T}">collection</see> to check.</param>
    /// <param name="predicate">The predicate.</param>
    /// <param name="messageFormat">A composite format string.</param>
    /// <param name="messageArguments">An array of message arguments to format.</param>
    [Conditional("DEBUG")]
    [StringFormatMethod("messageFormat")]
    public static void Contains<T>(
      [NotNull] IEnumerable<T> collection,
      [NotNull] Func<T, bool> predicate,
      [NotNull] string messageFormat,
      [CanBeNull] params object[] messageArguments)
    {
      string message = AssertionHelper.GetDetailMessage(messageFormat, messageArguments);

      Assert.IsTrue(collection.Where(predicate).Any(), message);
    }


    /// <summary>
    /// Asserts that a specified <see cref="IDictionary{TKey, TValue}"/> contains a specified key.
    /// </summary>
    /// <typeparam name="TKey">
    /// The type of the key.
    /// </typeparam>
    /// <typeparam name="TValue">
    /// The type of the value.
    /// </typeparam>
    /// <param name="dictionary">
    /// A <see cref="IDictionary{TKey, TValue}"/> to check.
    /// </param>
    /// <param name="key">
    /// A key to check.
    /// </param>
    /// <param name="messageFormat">
    /// A <see cref="System.String"/> containing composite message format.
    /// </param>
    /// <param name="messageArguments">
    /// An array of message arguments to format.
    /// </param>
    [Conditional("DEBUG")]
    [StringFormatMethod("detailMessageFormat")]
    public static void ContainsKey<TKey, TValue>(
      [NotNull] IDictionary<TKey, TValue> dictionary, 
      [NotNull] TKey key, 
      [NotNull] string messageFormat, 
      [CanBeNull] params object[] messageArguments)
    {
      string message = AssertionHelper.GetDetailMessage(messageFormat, messageArguments);

      Assert.IsTrue(dictionary.ContainsKey(key), message);
    }


    /// <summary>
    /// Asserts that the specified collection is empty.
    /// </summary>
    /// <typeparam name="T">
    /// Collection items type.
    /// </typeparam>
    /// <param name="collection">
    /// A <see cref="IEnumerable{T}">collection</see> to check.
    /// </param>
    /// <param name="messageFormat">
    /// A <see cref="System.String"/> message to display.
    /// </param>
    /// <param name="messageArguments">
    /// An array of message arguments to format.
    /// </param>
    [Conditional("DEBUG")]
    [StringFormatMethod("messageFormat")]
    public static void IsEmpty<T>(
      [NotNull] IEnumerable<T> collection, [NotNull] string messageFormat, [CanBeNull] params object[] messageArguments)
    {
      string detailMessage = AssertionHelper.GetDetailMessage(messageFormat, messageArguments);

      Assert.IsTrue(!collection.Any(), detailMessage);
    }


    /// <summary>
    /// Asserts that the specified collection is not empty.
    /// </summary>
    /// <typeparam name="T">
    /// Collection items type.
    /// </typeparam>
    /// <param name="collection">
    /// A <see cref="IEnumerable{T}">collection</see> to check.
    /// </param>
    /// <param name="messageFormat">
    /// A <see cref="System.String"/> message to display.
    /// </param>
    /// <param name="messageArguments">
    /// An array of message arguments to format.
    /// </param>
    [Conditional("DEBUG")]
    [StringFormatMethod("messageFormat")]
    public static void IsNotEmpty<T>(
      [NotNull] IEnumerable<T> collection, [NotNull] string messageFormat, [CanBeNull] params object[] messageArguments)
    {
      string detailMessage = AssertionHelper.GetDetailMessage(messageFormat, messageArguments);

      Assert.IsTrue(collection.Any(), detailMessage);
    }


    /// <summary>
    /// Asserts that a specified <see cref="IEnumerable{T}">collection</see> doesn't contain a specified item.
    /// </summary>
    /// <typeparam name="T">
    /// The type of collection items.
    /// </typeparam>
    /// <param name="collection">
    /// A <see cref="IEnumerable{T}">collection</see> to check.
    /// </param>
    /// <param name="item">
    /// An item to check.
    /// </param>
    /// <param name="messageFormat">
    /// A composite format string.
    /// </param>
    /// <param name="messageArguments">
    /// An array of message arguments to format.
    /// </param>
    [Conditional("DEBUG")]
    [StringFormatMethod("messageFormat")]
    public static void NotContains<T>(
      [NotNull] IEnumerable<T> collection, 
      [NotNull] T item, 
      [NotNull] string messageFormat, 
      [CanBeNull] params object[] messageArguments)
    {
      string detailMessage = AssertionHelper.GetDetailMessage(messageFormat, messageArguments);

      Assert.IsTrue(!collection.Contains(item), detailMessage);
    }


    /// <summary>
    /// Asserts that a specified <see cref="IEnumerable{T}">collection</see> doesn't contain an item defined by the predicate.
    /// </summary>
    /// <typeparam name="T">The type of collection items.</typeparam>
    /// <param name="collection">A <see cref="IEnumerable{T}">collection</see> to check.</param>
    /// <param name="predicate">The predicate.</param>
    /// <param name="messageFormat">A composite format string.</param>
    /// <param name="messageArguments">An array of message arguments to format.</param>
    [Conditional("DEBUG")]
    [StringFormatMethod("messageFormat")]
    public static void NotContains<T>(
      [NotNull] IEnumerable<T> collection,
      [NotNull] Func<T, bool> predicate,
      [NotNull] string messageFormat,
      [CanBeNull] params object[] messageArguments)
    {
      string message = AssertionHelper.GetDetailMessage(messageFormat, messageArguments);

      Assert.IsTrue(!collection.Where(predicate).Any(), message);
    }


    /// <summary>
    /// Asserts that a specified <see cref="IDictionary{TKey, TValue}"/> doesn't contain a specified key-value pair.
    /// </summary>
    /// <typeparam name="TKey">
    /// The type of the key.
    /// </typeparam>
    /// <typeparam name="TValue">
    /// The type of the value.
    /// </typeparam>
    /// <param name="dictionary">
    /// A <see cref="IDictionary{TKey, TValue}"/> to check.
    /// </param>
    /// <param name="key">
    /// A key to check.
    /// </param>
    /// <param name="value">
    /// A value to check.
    /// </param>
    /// <param name="messageFormat">
    /// A <see cref="System.String"/> containing composite message format.
    /// </param>
    /// <param name="messageArguments">
    /// An array of message arguments to format.
    /// </param>
    [Conditional("DEBUG")]
    [StringFormatMethod("detailMessageFormat")]
    public static void NotContains<TKey, TValue>(
      [NotNull] IDictionary<TKey, TValue> dictionary, 
      [NotNull] TKey key, 
      [NotNull] TValue value, 
      [NotNull] string messageFormat, 
      [CanBeNull] params object[] messageArguments)
    {
      var keyValuePair = new KeyValuePair<TKey, TValue>(key, value);
      string message = AssertionHelper.GetDetailMessage(messageFormat, messageArguments);

      Assert.IsTrue(!dictionary.Contains(keyValuePair), message);
    }


    /// <summary>
    /// Asserts that a specified <see cref="IDictionary{TKey, TValue}"/> doesn't contain a specified key.
    /// </summary>
    /// <typeparam name="TKey">
    /// The type of the key.
    /// </typeparam>
    /// <typeparam name="TValue">
    /// The type of the value.
    /// </typeparam>
    /// <param name="dictionary">
    /// A <see cref="IDictionary{TKey, TValue}"/> to check.
    /// </param>
    /// <param name="key">
    /// A key to check.
    /// </param>
    /// <param name="messageFormat">
    /// A <see cref="System.String"/> containing composite message format.
    /// </param>
    /// <param name="messageArguments">
    /// An array of message arguments to format.
    /// </param>
    [Conditional("DEBUG")]
    [StringFormatMethod("detailMessageFormat")]
    public static void NotContainsKey<TKey, TValue>(
      [NotNull] IDictionary<TKey, TValue> dictionary, 
      [NotNull] TKey key, 
      [NotNull] string messageFormat, 
      [CanBeNull] params object[] messageArguments)
    {
      string message = AssertionHelper.GetDetailMessage(messageFormat, messageArguments);

      Assert.IsTrue(!dictionary.ContainsKey(key), message);
    }

    #endregion
  }
}